package com.plugin.buildsrc.packing

import com.android.build.gradle.api.ApplicationVariant
import com.android.builder.model.ProductFlavor
import com.android.builder.model.SigningConfig
import com.android.zipflinger.Source
import com.android.zipflinger.ZipArchive
import com.android.zipflinger.ZipSource
import org.apache.commons.io.FileUtils
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskAction
import com.plugin.buildsrc.*

/*
 * 自定义打包任务
*/


class ApkPackingTask extends DefaultTask {
    @Input
    ApplicationVariant mVariant
    @InputFiles
    File mOutputDir
    @Input
    List<String> mChannels

    @InputDirectory
    File toolsDir = new File("..${File.separator}buildSrc${File.separator}tools")

    ApkPackingTask() {
        group "packingApk"
    }

    @TaskAction
    void packing() {
        System.out.println("开始打包...")

        File channelsDir = new File(mOutputDir, "channels")
        channelsDir.mkdirs()
        //转移->加固->对齐->签名->渠道
        File parentApk = reAlignApk(moveArtifact())
        //规整结构
        File originDir = new File(mOutputDir, "origin")
        originDir.mkdirs()
        ProductFlavor platform = mVariant.productFlavors.find {it.dimension=="platform"}
        ProductFlavor env = mVariant.productFlavors.find {it.dimension=="env"}
        File newParentApk = new File(originDir, "${platform.name}-${env.name}-${mVariant.versionName}.apk")
        parentApk.renameTo(newParentApk)
        fissionApksOfChannel(channelsDir, newParentApk)
        mOutputDir.eachFile {
            if (!it.isDirectory()) it.delete()
        }
    }

    //转移编译产物
    File moveArtifact() {
        logs("产物转移开始")
        FileUtils.deleteDirectory(mOutputDir)
        mOutputDir.mkdirs()
        //复制Apk文件夹
        File apkDir = new File(project.getBuildDir(), "outputs/apk/${mVariant.getDirName()}")
        FileUtils.copyDirectory(apkDir, mOutputDir)
        //复制Mapping文件夹 if need
//        if (mVariant.getBuildType().name == "release") {
//            File mappingSrcDir = new File(project.getBuildDir(), "outputs/mapping/${mVariant.getName()}")
//            File mappingDestDir = new File(mOutputDir, "mapping")
//            mappingDestDir.mkdirs()
//            FileUtils.copyDirectory(mappingSrcDir, mappingDestDir)
//        }
        logs("打包产物 全部已经转移至:${mOutputDir.getPath()}")
        logs("产物转移结束")
        return new File(mOutputDir, "${mVariant.outputs.first().outputFile.name}")
    }

    //制作 加固保护的Apk
    File makeProtectedApk(File apkOriginFile) {
        def args = [
                "\"itman@valorosoltd.com\"",
                "${apkOriginFile.path}",
                "${mOutputDir.path}",
                "\"\"",
                "458"
        ]
        logs("加固 开始...")
        Logger.startCount()
        File toolFile = new File(toolsDir, "encryptiontool-1.2_forsoapi.jar")
        logs("加固工具${toolFile.path} 是否存在: ${toolFile.exists()?"是":"否"}")
        execute("java -jar ${toolFile.path} ${args.join(" ")}", "爱加密")
        Logger.endCount("加固任务")
        logs("加固 结束...")

        List<File> apks = mOutputDir.listFiles().findAll {
            it.name.contains(".apk")
        }
        if(apks.size() < 2) {
            throw new GradleException("Apk加固异常")
        }
        File protectApkFile = new File(mOutputDir, "${apkOriginFile.name.replace(".apk", "_protected.apk")}")
        apks[1].renameTo(protectApkFile)
        if (!protectApkFile.exists()) throw new GradleException("Apk加固失败")
        return protectApkFile
    }

    //重新字节对齐
    File reAlignApk(File protectApkFile) {
        logs("对齐 开始...")
        Logger.startCount()
        File byteAlignApkFile = new File(mOutputDir, "${protectApkFile.name.replace(".apk", "_align.apk")}")
        def zipSource = new ZipSource(protectApkFile.toPath())
        zipSource.entries().each {
            def zipSourceEntry = zipSource.select(it.value.name, it.value.name, ZipSource.COMPRESSION_NO_CHANGE, 4L)
//            zipSourceEntry.align(4L)
        }
        ZipArchive zipArchive = new ZipArchive(byteAlignApkFile)
        zipArchive.add(zipSource)
        zipArchive.close()
        Logger.endCount("对齐")
        logs("对齐 结束...")
        if (!byteAlignApkFile.exists()) throw new GradleException("Apk对齐失败")
        return byteAlignApkFile
    }

    //重新签名
    File reSignApk(File alignedApkFile) {
        logs("签名开始")
        File finalApkFile = new File(mOutputDir, "${alignedApkFile.name.replace(".apk", "_signed.apk")}")
        SigningConfig signingConfig = ((ApplicationVariant) mVariant).getSigningConfig()
        def args = [
                "sign",
                "-v",
                "--ks",
                "${signingConfig.storeFile.path}",
                "--ks-key-alias",
                signingConfig.keyAlias,
                "--ks-pass",
                "pass:${signingConfig.storePassword}",
                "--key-pass",
                "pass:${signingConfig.keyPassword}",
                "--out",
                "${finalApkFile.path}",
                alignedApkFile.path,
        ]
        execute("java -jar ${toolsDir.path}${File.separator}apksigner.jar ${args.join(" ")}", "signapk")
        logs("签名结束")
        if (!finalApkFile.exists()) throw new GradleException("Apk签名失败")
        logs("删除对齐包: ${alignedApkFile.path}")
        alignedApkFile.delete()
        return finalApkFile
    }

    //分裂 频道包
    void fissionApksOfChannel(File channelsDir, File finalApkFile) {
        logs("制作渠道包开始...")
        logs("mChannels$mChannels")
        Logger.startCount()
        File channelApkDir = channelsDir
        channelApkDir.mkdirs()
        execute("java -jar ${toolsDir}${File.separator}channel_cli.jar put -c \"${mChannels.join(",")}\" ${finalApkFile.path} ${channelApkDir.path}", "渠道包")
        Logger.endCount("渠道包")
        logs("制作渠道包结束...")
    }

    void execute(String cmd, String tag) {
        Process proc = cmd.execute();
        Reader reader = proc.in.newReader("GBK")
        Reader errReader = proc.err.newReader("GBK")
        String tmp
        while ((tmp = reader.readLine()) != null || (tmp = errReader.readLine()) != null) {
            logs("${tag}:: " + tmp)
        }
        reader.close()
        errReader.close()
    }

    void logs(String content) {
        println("${ApkPackingTask.simpleName}--> ${content}")
    }
}
